﻿using SixLabors.Fonts;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Drawing.Processing;
using SixLabors.ImageSharp.PixelFormats;
using SixLabors.ImageSharp.Processing;
using System.Numerics;

namespace MicroCloud.Utils
{
    /// <summary>
    /// 验证码生成类
    /// </summary>
    public class ValidateCoder
    {
        //字段
        private static readonly Random Random = new();

        /// <summary>
        /// 初始化验证码生成类 <see cref="ValidateCoder"/> 的新实例
        /// </summary>
        public ValidateCoder()
        {
            //字体类型
            FontTypes = new List<FontType> {
                FontType.Comfortaa_bold,
                FontType.Denkiya_san,
                FontType.Dingtalk_san,
            };

            FontSize = 20;
            FontWidth = FontSize;
            BgColor = System.Drawing.Color.FromArgb(240, 240, 240);
            RandomPointPercent = 0;
        }

        #region 属性
        /// <summary>
        /// 获取或设置 字体类型集合
        /// </summary>
        public List<FontType> FontTypes { get; set; }

        /// <summary>
        /// 获取或设置 字体大小
        /// </summary>
        public int FontSize { get; set; }

        /// <summary>
        /// 获取或设置 字体宽度
        /// </summary>
        public int FontWidth { get; set; }

        /// <summary>
        /// 获取或设置 图片高度
        /// </summary>
        public int Height { get; set; }

        /// <summary>
        /// 获取或设置 背景颜色
        /// </summary>
        public System.Drawing.Color BgColor { get; set; }

        /// <summary>
        /// 获取或设置 是否有边框
        /// </summary>
        public bool HasBorder { get; set; }

        /// <summary>
        /// 获取或设置 是否随机位置
        /// </summary>
        public bool RandomPosition { get; set; }

        /// <summary>
        /// 获取或设置 是否随机字体颜色
        /// </summary>
        public bool RandomColor { get; set; }

        /// <summary>
        /// 获取或设置 是否随机倾斜字体
        /// </summary>
        public bool RandomItalic { get; set; }

        /// <summary>
        /// 获取或设置 随机干扰点百分比（百分数形式）
        /// </summary>
        public decimal RandomPointPercent { get; set; }

        /// <summary>
        /// 获取或设置 随机干扰线数量
        /// </summary>
        public int RandomLineCount { get; set; }

        #endregion

        /// <summary>
        /// 获取指定长度的验证码字符串
        /// </summary>
        /// <param name="length">验证码字符长度</param>
        /// <param name="codeType">验证码类型</param>
        /// <returns></returns>
        public string GetCode(int length, ValidateCodeType codeType = ValidateCodeType.NumberAndLetter)
        {
            length.CheckGreaterThan(nameof(length), 0);

            return codeType switch
            {
                ValidateCodeType.Number => GetRandomNums(length),
                ValidateCodeType.Hanzi => GetRandomHanzis(length),
                ValidateCodeType.NumberAndLetter => GetRandomNumsAndLetters(length),
                _ => GetRandomNumsAndLetters(length),
            };
        }

        /// <summary>
        /// 获取指定字符串的验证码图片
        /// </summary>
        /// <param name="code">验证码内容</param>
        /// <returns></returns>
        public Image CreateImage(string code)
        {
            code.CheckNotNullOrEmpty(nameof(code));

            int width = FontWidth * code.Length + FontWidth;
            int height = FontSize + FontSize / 2;
            const int flag = 255 / 2;
            bool isBgLight = (BgColor.R + BgColor.G + BgColor.B) / 3 > flag;
            //绘制图片
            Image<Rgba32> image = new(width, height);   //画布大小
            var brush = new SolidBrush(Color.FromRgb((byte)(255 - BgColor.R), (byte)(255 - BgColor.G), (byte)(255 - BgColor.B)));
            int x, y = 3;
            Random rnd = Random;
            image.Mutate(ctx =>
            {
                //画布背景
                ctx.BackgroundColor(Color.FromRgb(BgColor.R, BgColor.G, BgColor.B));
                //画布边框
                if (HasBorder)
                {
                    //o.DrawPolygon(new Pen(Color.Silver, 1), new PointF(0, 0), new PointF(0, image.Width - 1), new PointF(image.Height - 1, 0), new PointF(image.Height - 1, image.Width - 1));
                    ctx.DrawPolygon(Pens.Solid(Color.Silver, 1), new PointF[]
                    {
                        new Vector2(0, 0),
                        new Vector2(0, image.Width - 1),
                        new Vector2(image.Height - 1, 0),
                        new Vector2(10, 10),
                    });
                }
                //绘制干扰线
                for (int i = 0; i < RandomLineCount; i++)
                {
                    x = rnd.Next(image.Width);
                    y = rnd.Next(image.Height);
                    int m = rnd.Next(image.Width);
                    int n = rnd.Next(image.Height);
                    Color lineColor = !RandomColor
                        ? Color.FromRgb(90, 90, 90)
                        : isBgLight
                            ? Color.FromRgb((byte)rnd.Next(130, 200), (byte)rnd.Next(130, 200), (byte)rnd.Next(130, 200))
                            : Color.FromRgb((byte)rnd.Next(70, 150), (byte)rnd.Next(70, 150), (byte)rnd.Next(70, 150));
                    ctx.DrawLine(lineColor, 2, new PointF[]
                    {
                        new Vector2(x, y),
                        new Vector2(m, n),
                    });

                    //Pen pen = new(lineColor, 2);
                    ////o.DrawLines(pen, new PointF(x, y), new PointF(m, n));
                    //ctx.DrawLines(pen, new PointF[]
                    //{
                    //    new Vector2(x, y),
                    //    new Vector2(m, n),
                    //});
                }
                //绘制干扰点
                for (int i = 0; i < (int)(image.Width * image.Height * RandomPointPercent / 100); i++)
                {
                    x = rnd.Next(image.Width);
                    y = rnd.Next(image.Height);
                    Color pointColor = isBgLight
                        ? Color.FromRgb((byte)rnd.Next(30, 80), (byte)rnd.Next(30, 80), (byte)rnd.Next(30, 80))
                        : Color.FromRgb((byte)rnd.Next(150, 200), (byte)rnd.Next(150, 200), (byte)rnd.Next(150, 200));
                    image[x, y] = pointColor;
                }
                //绘制文字
                for (int i = 0; i < code.Length; i++)
                {
                    rnd = Random;
                    x = FontWidth / 4 + FontWidth * i;
                    if (RandomPosition)
                    {
                        x = rnd.Next(FontWidth / 4) + FontWidth * i;
                        y = rnd.Next(image.Height / 5);
                    }
                    PointF point = new(x, y);
                    if (RandomColor)
                    {
                        int r, g, b;
                        if (!isBgLight)
                        {
                            r = rnd.Next(255 - BgColor.R);
                            g = rnd.Next(255 - BgColor.G);
                            b = rnd.Next(255 - BgColor.B);
                            if ((r + g + b) / 3 < flag)
                            {
                                r = 255 - r;
                                g = 255 - g;
                                b = 255 - b;
                            }
                        }
                        else
                        {
                            r = rnd.Next(BgColor.R);
                            g = rnd.Next(BgColor.G);
                            b = rnd.Next(BgColor.B);
                            if ((r + g + b) / 3 > flag)
                            {
                                r = 255 - r;
                                g = 255 - g;
                                b = 255 - b;
                            }
                        }
                        brush = new SolidBrush(Color.FromRgb((byte)r, (byte)g, (byte)b));
                    }
                    // 字体
                    //var font = SystemFonts.CreateFont(SystemFonts.Families.First().Name, FontSize, FontStyle.Bold);
                    //FontType fontType = codeType == ValidateCodeType.Hanzi ? FontTypesForHanzi[rnd.Next(FontTypesForHanzi.Count)] : FontTypes[rnd.Next(FontTypes.Count)];
                    FontType fontType = FontTypes[rnd.Next(FontTypes.Count)];
                    var font = new Font(FontsHandler.FontFamilys[fontType], FontSize);
                    var drawingOptions = new DrawingOptions();
                    if (RandomItalic)
                    {
                        drawingOptions.Transform = Matrix3x2.CreateSkew(Convert.ToSingle(rnd.Next(2, 9) / 10d - 0.5), 0.001f);
                    }
                    //画字
                    ctx.DrawText(code.Substring(i, 1), font, brush, point);
                }
            });

            return image;

            /****** 项目图形处理已由“System.Drawing.Common”切换至“SixLabors.ImageSharp”，相关方法文件暂留备查 ******/
            #region "项目图形处理已由“System.Drawing.Common”切换至“SixLabors.ImageSharp”，相关方法文件暂留备查"
            //Bitmap image = new(width, height);
            //Graphics graph = Graphics.FromImage(image);
            //graph.Clear(BgColor);
            //Brush brush = new SolidBrush(Color.FromArgb(255 - BgColor.R, 255 - BgColor.G, 255 - BgColor.B));
            //int x, y = 3;
            //if (HasBorder)
            //{
            //    graph.DrawRectangle(new Pen(Color.Silver), 0, 0, image.Width - 1, image.Height - 1);
            //}

            //Random rnd = Random;

            ////绘制干扰线
            //for (int i = 0; i < RandomLineCount; i++)
            //{
            //    x = rnd.Next(image.Width);
            //    y = rnd.Next(image.Height);
            //    int m = rnd.Next(image.Width);
            //    int n = rnd.Next(image.Height);
            //    Color lineColor = !RandomColor
            //        ? Color.FromArgb(90, 90, 90)
            //        : isBgLight
            //            ? Color.FromArgb(rnd.Next(130, 200), rnd.Next(130, 200), rnd.Next(130, 200))
            //            : Color.FromArgb(rnd.Next(70, 150), rnd.Next(70, 150), rnd.Next(70, 150));
            //    Pen pen = new(lineColor, 2);
            //    graph.DrawLine(pen, x, y, m, n);
            //}

            ////绘制干扰点
            //for (int i = 0; i < (int)(image.Width * image.Height * RandomPointPercent / 100); i++)
            //{
            //    x = rnd.Next(image.Width);
            //    y = rnd.Next(image.Height);
            //    Color pointColor = isBgLight
            //        ? Color.FromArgb(rnd.Next(30, 80), rnd.Next(30, 80), rnd.Next(30, 80))
            //        : Color.FromArgb(rnd.Next(150, 200), rnd.Next(150, 200), rnd.Next(150, 200));
            //    image.SetPixel(x, y, pointColor);
            //}

            ////绘制文字
            //for (int i = 0; i < code.Length; i++)
            //{
            //    rnd = Random;
            //    x = FontWidth / 4 + FontWidth * i;
            //    if (RandomPosition)
            //    {
            //        x = rnd.Next(FontWidth / 4) + FontWidth * i;
            //        y = rnd.Next(image.Height / 5);
            //    }
            //    PointF point = new PointF(x, y);
            //    if (RandomColor)
            //    {
            //        int r, g, b;
            //        if (!isBgLight)
            //        {
            //            r = rnd.Next(255 - BgColor.R);
            //            g = rnd.Next(255 - BgColor.G);
            //            b = rnd.Next(255 - BgColor.B);
            //            if ((r + g + b) / 3 < flag)
            //            {
            //                r = 255 - r;
            //                g = 255 - g;
            //                b = 255 - b;
            //            }
            //        }
            //        else
            //        {
            //            r = rnd.Next(BgColor.R);
            //            g = rnd.Next(BgColor.G);
            //            b = rnd.Next(BgColor.B);
            //            if ((r + g + b) / 3 > flag)
            //            {
            //                r = 255 - r;
            //                g = 255 - g;
            //                b = 255 - b;
            //            }
            //        }
            //        brush = new SolidBrush(Color.FromArgb(r, g, b));
            //    }
            //    string fontName = codeType == ValidateCodeType.Hanzi
            //        ? FontNamesForHanzi[rnd.Next(FontNamesForHanzi.Count)]
            //        : FontNames[rnd.Next(FontNames.Count)];
            //    Font font = new Font(fontName, FontSize, FontStyle.Bold);
            //    if (RandomItalic)
            //    {
            //        graph.TranslateTransform(0, 0);
            //        Matrix transform = graph.Transform;
            //        transform.Shear(Convert.ToSingle(rnd.Next(2, 9) / 10d - 0.5), 0.001f);
            //        graph.Transform = transform;
            //    }
            //    graph.DrawString(code.Substring(i, 1), font, brush, point);
            //    graph.ResetTransform();
            //}

            //return image;
            #endregion

        }

        /// <summary>
        /// 获取指定长度的验证码图片
        /// </summary>
        /// <param name="length"></param>
        /// <param name="code"></param>
        /// <param name="codeType"></param>
        /// <returns></returns>
        public Image CreateImage(int length, out string code, ValidateCodeType codeType = ValidateCodeType.NumberAndLetter)
        {
            length.CheckGreaterThan(nameof(length), 0);

            length = length < 1 ? 1 : length;
            code = codeType switch
            {
                ValidateCodeType.Number => GetRandomNums(length),
                ValidateCodeType.Hanzi => GetRandomHanzis(length),
                ValidateCodeType.NumberAndLetter => GetRandomNumsAndLetters(length),
                _ => GetRandomNumsAndLetters(length),
            };
            if (code.Length > length)
            {
                //code = code.Substring(0, length);
                code = code[..length];
            }
            return CreateImage(code);
        }

        #region 私有方法
        /// <summary>
        /// 获取随机数
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        private static string GetRandomNums(int length)
        {
            int[] ints = new int[length];
            for (int i = 0; i < length; i++)
            {
                ints[i] = Random.Next(0, 9);
            }
            return ints.ExpandAndToString("");
        }

        /// <summary>
        /// 随机获取数字和字母
        /// </summary>
        /// <param name="length"></param>
        /// <returns></returns>
        private static string GetRandomNumsAndLetters(int length)
        {
            const string allChar = "2,3,4,5,6,7,8,9," +
                "A,B,C,D,E,F,G,H,J,K,M,N,P,Q,R,S,T,U,V,W,X,Y,Z," +
                "a,b,c,d,e,f,g,h,k,m,n,p,q,r,s,t,u,v,w,x,y,z";
            string[] allChars = allChar.Split(',');
            List<string> result = new List<string>();
            while (result.Count < length)
            {
                int index = Random.Next(allChars.Length);
                string c = allChars[index];
                result.Add(c);
            }
            return result.ExpandAndToString("");
        }

        /// <summary>
        /// 获取汉字验证码
        /// </summary>
        /// <param name="length">验证码长度</param>
        /// <returns></returns>
        private static string GetRandomHanzis(int length)
        {
            //汉字编码的组成元素，十六进制数
            string[] baseStrs = "0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f".Split(',');
            Encoding encoding = Encoding.GetEncoding("GB2312");
            string result = null;

            //每循环一次产生一个含两个元素的十六进制字节数组，并放入bytes数组中
            //汉字由四个区位码组成，1、2位作为字节数组的第一个元素，3、4位作为第二个元素
            for (int i = 0; i < length; i++)
            {
                Random rnd = Random;
                int index1 = rnd.Next(11, 14);
                string str1 = baseStrs[index1];

                int index2 = index1 == 13 ? rnd.Next(0, 7) : rnd.Next(0, 16);
                string str2 = baseStrs[index2];

                int index3 = rnd.Next(10, 16);
                string str3 = baseStrs[index3];

                int index4 = index3 == 10 ? rnd.Next(1, 16) : (index3 == 15 ? rnd.Next(0, 15) : rnd.Next(0, 16));
                string str4 = baseStrs[index4];

                //定义两个字节变量存储产生的随机汉字区位码
                byte b1 = Convert.ToByte(str1 + str2, 16);
                byte b2 = Convert.ToByte(str3 + str4, 16);
                byte[] bs = { b1, b2 };

                result += encoding.GetString(bs);
            }
            return result;
        }

        #endregion

    }
}